package mythread;

/**
 * @Auther: cty
 * @Date: 2020/1/5 21:13
 * @Description: 线程同步——方法同步
 * 1、为什么需要线程同步？
 * 在并发操作时，可能会出现线程不安全的问题。
 * 2、什么是并发？
 * 同一个对象同时被多个线程操作。
 * 3、线程不安全问题都有哪些？
 * （1）共享资源时，一个代理线程还未来得及“改”访问的工作空间中的资源，其他代理线程又去访问（读 或 改）该资源
 * （2）非共享资源时，一个线程将主内存中的资源拷贝到自己的工作空间还未来得及“改”回主内存，其他线程又将该资源从主内存拷贝到自己的工作空间进行访问（读 或 改）
 * 4、什么时候需要进行线程同步？
 * 当涉及到多个线程“改”数据时需要线程同步。
 * 5、如何实现线程同步？
 * 形成队列和锁机制，具体分为方法同步和块同步
 * （1）方法同步：synchronized void test(){方法体}
 * 非静态方法锁定当前对象this，静态对象锁定类的字节码对象（或称类的模子）User.class
 * （2）块同步：synchronized(obj){同步块}
 * 锁定obj，只能有一个，可以是任意数据类型，但尽量使用对象，
 * 一是因为基本数据类型值若相同可能指向相同的地址，
 * 二是若基本数据类型的值在变其地址（对象）也在变，对象锁不住
 * @version: 1.0
 */
public class He_SynMethodTest1_1 implements Runnable{
    private int ticketsNum = 99;  //剩余车票数
    private boolean flag = true;

    @Override
    public void run() {
        while(flag){
            //模拟网络延迟
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            test();
        }
    }

    //线程安全 方法同步（只能锁定this的资源，即this是锁定监视器）
    synchronized void test(){
        if(ticketsNum <= 0){  //【打印】
            flag = false;
        }else {
            //模拟网络延迟
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"售出了 "+ticketsNum--+" 号票,剩余 "+ticketsNum+"张票");  //【打印】和【修改】
        }
    }

    public static void main(String[] args) {
        //一份任务
        He_SynMethodTest1_1 ticket = new He_SynMethodTest1_1();
        //多个代理
        new Thread(ticket,"窗口1").start();  //第一个new Thread开辟工作空间，其他代理线程共享工作空间中的资源
        new Thread(ticket,"窗口2").start();
        new Thread(ticket,"窗口3").start();
    }
}

/*结果
窗口2售出了 9 号票,剩余 8张票
窗口1售出了 8 号票,剩余 7张票
窗口3售出了 7 号票,剩余 6张票
窗口1售出了 6 号票,剩余 5张票
窗口2售出了 5 号票,剩余 4张票
窗口1售出了 4 号票,剩余 3张票
窗口3售出了 3 号票,剩余 2张票
窗口1售出了 2 号票,剩余 1张票
窗口2售出了 1 号票,剩余 0张票
*/
